using System;
using System.Collections.Generic;
using System.Text;

namespace RapidHDL
{
    class MicroCoreComponent : Component 
    {

        MicroCoreTable oTable;

        public NodeVector I_InstructionReg;
        public Dictionary<string, NodeVector> O_ControlLines = new Dictionary<string,NodeVector>();
        public Dictionary<string, NodeVector> I_StatusBits = new Dictionary<string,NodeVector>();
        int iAddrWidth = 0;
        int iDataWidth = 0;

        ClockComponent oClock;
        NodeVector nvReset;

        ResetReg oDataReg;
        ResetReg oPCReg;
        StateMachine oStateMachine;

        NodeVector nvJump;
        NodeVector nvBranch;
        NodeVector nvDone;
        NodeVector nvControl;

        public NodeVector O_PC_NEXT;


        public MicroCoreComponent(Component poParentComponent, string psName, MicroCoreTable poTable, ClockComponent poClock, NodeVector pnvReset)
            : base(poParentComponent,psName)
        {
            oTable = poTable;
            iDataWidth = oTable.Width;
            iAddrWidth = Conversion.MinBitWidth(oTable.ROM.Count-1);
            I_InstructionReg = this.CreateNodeVector("IR_I", iAddrWidth);
            O_PC_NEXT = this.CreateNodeVector("PC_NEXT_O", 1);

            foreach (string sControlState in oTable.ControlStates.Keys)
            {
                string sName = sControlState + "_O";
                NodeVector nvBit = this.CreateNodeVector(sName, 1);
                O_ControlLines.Add(sName, nvBit);
            }
            foreach (string sStatusBit in oTable.StatusBits.Keys)
            {
                string sName = sStatusBit + "_I";
                NodeVector nvBit = this.CreateNodeVector(sName, 1);
                I_StatusBits.Add(sName, nvBit);
            }
            oClock = poClock;
            nvReset = pnvReset;
        }

        public override void GenerateStructure()
        {
            oDataReg = new ResetReg(this, "udr", oClock, iDataWidth, false, true,false);
            oDataReg.ResetIn.Join(nvReset);
            oDataReg.ResetTo.Join(0);

            oPCReg = new ResetReg(this, "upc", oClock, iAddrWidth, false, true,false);
            oPCReg.ResetIn.Join(nvReset);
            oPCReg.ResetTo.Join(0);

            ALUComponent oInc = new ALUComponent(this, "inc_pc", ALUType.Inc, iAddrWidth);

            ROM oROM = new ROM(this, "uROM", iDataWidth, oTable.ROM.Count);
            oROM.AddTable(oTable.ROM);

            Decoder oDecode = new Decoder(this, "ctl_decode",Conversion.MinBitWidth(oTable.ControlStates.Count - 1), oTable.ControlStates.Count);

            Mux oStatusMux = new Mux(this, "status_mux", 1, oTable.StatusBits.Count);

            Mux oPCMux = new Mux(this, "pc_mux", iAddrWidth, 3);

            NodeVector nvInstBitHigh = this.CreateNodeVector("C_inst_bit_high", 1);
            NodeVector nvInstBitLow = this.CreateNodeVector("C_inst_bit_low", 1);
            NodeVector nvDROut = this.CreateNodeVector("dr_out",iDataWidth - 2);
            NodeVector nvEnableControl = this.CreateNodeVector("C_disable_ctl", 1);
            NodeVector nvLatchDR = this.CreateNodeVector("C_latch_dr", 1);
            NodeVector nvLatchPC = this.CreateNodeVector("C_latch_pc", 1);
            NodeVector nvStatusBit = this.CreateNodeVector("C_status", 1);

            nvInstBitHigh.Join(oDataReg.OutputNodes[iDataWidth - 1, iDataWidth - 1]);
            nvInstBitLow.Join(oDataReg.OutputNodes[iDataWidth - 2, iDataWidth - 2]);
            nvDROut.Join(oDataReg.OutputNodes[0, iDataWidth - 3]);
            nvLatchDR.Join(oDataReg.EnableNodes);
            nvLatchPC.Join(oPCReg.EnableNodes);
            nvStatusBit.Join(oStatusMux.OutputNodes);

            foreach (string sControlState in oTable.ControlStates.Keys)
            {
                int iBit = oTable.ControlStates[sControlState];
                string sName = sControlState + "_enable";
                AndGate oAndGate = new AndGate(this,sName,2);
                oAndGate.InputNodes[0,0].Join(oDecode.OutputNodes[iBit,iBit]);
                oAndGate.InputNodes[1,1].Join(nvEnableControl);
                oAndGate.OutputNodes.Join(O_ControlLines[sControlState + "_O"]);
            }
            foreach (string sStatusBit in oTable.StatusBits.Keys)
            {
                int iBit = oTable.StatusBits[sStatusBit];
                I_StatusBits[sStatusBit + "_I"].Join(oStatusMux.InputNodes[iBit]);
            }
            oStatusMux.SelectNodes.Join(nvDROut);

            oPCMux.InputNodes[0].Join(I_InstructionReg);
            oPCMux.InputNodes[1].Join(oInc.O_A);
            oInc.I_A.Join(oPCReg.OutputNodes);
            oPCMux.InputNodes[2].Join(nvDROut);
            oPCMux.OutputNodes.Join(oPCReg.InputNodes);

            oPCReg.OutputNodes.Join(oROM.I_Address);
            oDataReg.InputNodes.Join(oROM.O_Value);

            oDecode.InputNodes.Join(nvDROut);

            oStateMachine = new StateMachine(this, "state_ctlr", oClock);
            oStateMachine.ResetIn.Join(nvReset);
            oStateMachine.AddState("state_reset");
            oStateMachine.AddState("state_dr");
            oStateMachine.AddState("state_jump");
            oStateMachine.AddState("state_skip_branch");
            oStateMachine.AddState("state_ir");
            //oStateMachine.AddState("state_ir_delay");
            //oStateMachine.AddState("state_ir_delay_2");
            //oStateMachine.AddState("state_ir_delay_3");

            oStateMachine.SetDefaultState("state_reset");

            nvJump = nvInstBitHigh & !nvInstBitLow;
            nvBranch = nvInstBitHigh & nvInstBitLow;
            nvDone = !nvInstBitHigh & nvInstBitLow;
            nvControl = !nvInstBitHigh & !nvInstBitLow;

            oStateMachine.StateTransition("state_reset","state_ir");
            oStateMachine.StateTransition("state_ir", "state_dr");
            oStateMachine.StateTransition("state_dr",nvJump,"state_jump");
            oStateMachine.StateTransition("state_jump","state_dr");
            oStateMachine.StateTransition("state_dr", nvBranch & !nvStatusBit, "state_skip_branch");
            oStateMachine.StateTransition("state_skip_branch", "state_dr");
            oStateMachine.StateTransition("state_dr", nvDone, "state_ir");
            //oStateMachine.StateTransition("state_ir_delay", "state_ir_delay_2");
            //oStateMachine.StateTransition("state_ir_delay_3", "state_ir");
            //oStateMachine.StateTransition("state_ir_delay_2", "state_ir");
            oStateMachine.StateTransition("state_dr", nvControl, "state_dr");
            oStateMachine.StateTransition("state_dr", nvBranch & nvStatusBit, "state_dr");

            nvEnableControl.Join(oStateMachine.StateCurrent("state_dr") & (nvControl | nvDone));

            nvLatchDR.Join(!oStateMachine.StateCurrent("state_reset"));
            nvLatchPC.Join(nvLatchDR);

            AssignmentMux oAssign = new AssignmentMux(this, "ir_state_mux", 2, Operators.CONST(this,0,2) );
            oAssign.OutputNodes.Join(oPCMux.SelectNodes);
            oAssign.AddAssignment(oStateMachine.StateCurrent("state_dr"), Operators.CONST(this,1,2));
            oAssign.AddAssignment(oStateMachine.StateCurrent("state_skip_branch"), Operators.CONST(this, 1, 2));
            oAssign.AddAssignment(oStateMachine.StateCurrent("state_jump"), Operators.CONST(this, 2, 2));

            Utility.SourceExtraBits(this, oPCMux.InputNodes[2], 0);
            //Utility.SourceExtraBits(this, oPCMux.InputNodes[0], 0);

            O_PC_NEXT.Join(oStateMachine.StateCurrent("state_ir"));
        }

        public void AddSignalSources()
        {
            RapidHardware.SystemInterface.AddSignalSource(Name + "_IR", I_InstructionReg, 0);
            foreach (string sStatus in I_StatusBits.Keys)
            {
                RapidHardware.SystemInterface.AddSignalSource(Name + "_" + sStatus, I_StatusBits[sStatus],0);
            }
        }

        public void AddSignalViews()
        {
            //bool bMinimal = true;
            bool bMinimal = false;
            SignalView oSignalView;
            if (!bMinimal)
            {
                foreach (string sControlState in O_ControlLines.Keys)
                {
                    oSignalView = RapidHardware.SystemInterface.AddSignalView(Name + "_" + sControlState, O_ControlLines[sControlState]);
                    oSignalView.Hidden = true;
                }

                oSignalView = RapidHardware.SystemInterface.AddSignalView(Name + "_dr", oDataReg.OutputNodes);
                oSignalView.Hidden = true;
                oSignalView = RapidHardware.SystemInterface.AddSignalView(Name + "_pc", oPCReg.OutputNodes);
                oSignalView.Hidden = true;
                oSignalView = RapidHardware.SystemInterface.AddSignalView(Name + "_jump", nvJump);
                oSignalView.Hidden = true;
                oSignalView = RapidHardware.SystemInterface.AddSignalView(Name + "_branch", nvBranch);
                oSignalView.Hidden = true;
                oSignalView = RapidHardware.SystemInterface.AddSignalView(Name + "_done", nvDone);
                oSignalView.Hidden = true;
                oSignalView = RapidHardware.SystemInterface.AddSignalView(Name + "_control", nvControl);
                oSignalView.Hidden = true;

                oSignalView = RapidHardware.SystemInterface.AddSignalView(Name + "_state_reset", oStateMachine.StateCurrent("state_reset"));
                oSignalView.Hidden = true;
                oSignalView = RapidHardware.SystemInterface.AddSignalView(Name + "_state_dr", oStateMachine.StateCurrent("state_dr"));
                oSignalView.Hidden = true;
                oSignalView = RapidHardware.SystemInterface.AddSignalView(Name + "_state_jump", oStateMachine.StateCurrent("state_jump"));
                oSignalView.Hidden = true;
                oSignalView = RapidHardware.SystemInterface.AddSignalView(Name + "_state_skip_branch", oStateMachine.StateCurrent("state_skip_branch"));
                oSignalView.Hidden = true;
                oSignalView = RapidHardware.SystemInterface.AddSignalView(Name + "_state_ir", oStateMachine.StateCurrent("state_ir"));
                oSignalView.Hidden = true;
            }
            else
            {
                oSignalView = RapidHardware.SystemInterface.AddSignalView(Name + "_state_ir", oStateMachine.StateCurrent("state_ir"));
                oSignalView.Hidden = true;
            }
        }

        public void SignalDisplay(HardwareScript poScript, int piClockTime)
        {
            //poScript.Echo(piClockTime, "---------------------------------------------------------");            
            poScript.Echo(piClockTime,Name + " Registers");
            poScript.SignalView(piClockTime, Name + "_dr", SignalFormat.Unsigned);
            poScript.SignalView(piClockTime, Name + "_pc", SignalFormat.Unsigned);
            poScript.Echo(piClockTime,"---");
            poScript.Echo(piClockTime, Name + " State");            
            poScript.SignalView(piClockTime, Name + "_state_reset", SignalFormat.Binary, "1");
            poScript.SignalView(piClockTime, Name + "_state_dr", SignalFormat.Binary, "1");
            poScript.SignalView(piClockTime, Name + "_state_jump", SignalFormat.Binary, "1");
            poScript.SignalView(piClockTime, Name + "_state_skip_branch", SignalFormat.Binary, "1");
            poScript.SignalView(piClockTime, Name + "_state_ir", SignalFormat.Binary, "1");
            poScript.Echo(piClockTime,"---");
            poScript.Echo(piClockTime, Name + " Instruction Type");            
            poScript.SignalView(piClockTime, Name + "_jump", SignalFormat.Binary, "1");
            poScript.SignalView(piClockTime, Name + "_branch", SignalFormat.Binary, "1");
            poScript.SignalView(piClockTime, Name + "_done", SignalFormat.Binary, "1");
            poScript.SignalView(piClockTime, Name + "_control", SignalFormat.Binary, "1");
            poScript.Echo(piClockTime,"---");
            poScript.Echo(piClockTime, "Control Lines Out");

            foreach (string sControlState in O_ControlLines.Keys)
            {
                poScript.SignalView(piClockTime, Name + "_" + sControlState, SignalFormat.Binary, "1");            
            }
            poScript.Echo(piClockTime, "_______________________________________________________");

        }

        public void SignalAddress(HardwareScript poScript, int piClockTime, int piValue)
        {
            poScript.SignalOut(piClockTime, Name + "_IR", piValue.ToString(), SignalFormat.Unsigned);
        }

        public void SignalStatusBit(HardwareScript poScript, int piClockTime, string psStatusBit, int piValue)
        {
            poScript.SignalOut(piClockTime, Name + "_" + psStatusBit + "_I", piValue.ToString(), SignalFormat.Unsigned);
        }

    }
}
